import requests
import re
import json
import time
import random
import muggle_ocr
import urllib3
import rsa
import binascii
import base64

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


class sn_login:
    def __init__(self, username, password, proxy='', sdk=''):
        self.username = username
        self.password = password
        if proxy:
            self.proxy = {'http': proxy, 'https': proxy}
        else:
            self.proxy = ''
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36'
        }
        self.sess = requests.session()
        if not sdk:
            # muggle_ocr模块，用于训练模型的快速部署(训练好的微博验证码模型在weibo_code文件夹)
            self.sdk = muggle_ocr.SDK(conf_path='./weibo_code/wb-CNNX-GRU-H64-CTC-C1_model.yaml')
        else:
            self.sdk = sdk
        self.server_time = ''
        self.code = ''
        self.uid = ''
        self.nonce = ''
        self.pubkey = ''
        self.rsakv = ''
        self.su = ''
        self.ticket_url = ''
        self.cookiesdict = {}
        self.cookiesdict.setdefault('username', self.username)
        self.login_flog = True
        self.proxy_flag = True

    def get_code(self):
        """
        验证码获取识别
        """
        url = 'https://login.sina.com.cn/cgi/pin.php?'
        params = {
            'r': str(random.randint(0, 99999999)).zfill(8),
            's': '0',
        }
        if self.proxy:
            pngs = self.sess.get(url=url, params=params, proxies=self.proxy, verify=False).content
        else:
            pngs = self.sess.get(url=url, params=params, verify=False).content
        # 验证码识别
        self.code = self.sdk.predict(image_bytes=pngs)

    def before_login(self):
        """
        登录前请求，返回部分参数登录时使用
        """
        self.su = base64.b64encode(self.username.encode('utf-8')).decode('utf-8')
        url = 'https://login.sina.com.cn/sso/prelogin.php?'
        self.headers['Host'] = 'login.sina.com.cn'
        self.headers['Referer'] = 'https://login.sina.com.cn/signup/signin.php'
        params = {
            'entry': 'account',
            'callback': 'sinaSSOController.preloginCallBack',
            'su': self.su,
            'rsakt': 'mod',
            'client': 'ssologin.js(v1.4.15)',
            '_': int(time.time() * 1000),
        }
        if self.proxy:
            resp = self.sess.get(url=url, headers=self.headers, proxies=self.proxy, params=params, verify=False)
        else:
            resp = self.sess.get(url=url, headers=self.headers, params=params, verify=False)
        content = json.loads(re.findall('sinaSSOController.preloginCallBack\((.*?)\)', resp.text)[0])
        self.server_time = content.get('servertime')
        self.nonce = content.get('nonce')
        self.pubkey = content.get('pubkey')
        self.rsakv = content.get('rsakv')

    def login(self):
        """
        登录请求
        """
        set_key = rsa.PublicKey(int(self.pubkey, 16), 65537)
        encrypt_str = str(int(time.time())) + '\t' + self.nonce + '\n' + self.password
        # sp是password的rsa加密
        sp = binascii.b2a_hex(rsa.encrypt(encrypt_str.encode('utf8'), set_key)).decode('utf-8')
        data = {
            'entry': 'account',
            'gateway': '1',
            'from': 'null',
            'savestate': '30',
            'useticket': '0',
            'pagerefer': '',
            'vsnf': '1',
            'door': self.code,
            'su': self.su,
            'service': 'account',
            'servertime': self.server_time,
            'nonce': self.nonce,
            'pwencode': 'rsa2',
            'rsakv': self.rsakv,
            'sp': sp,
            'sr': '1536*864',
            'encoding': 'UTF-8',
            'cdult': '3',
            'domain': 'sina.com.cn',
            'prelt': '53',
            'returntype': 'TEXT',
        }
        if self.proxy:
            resp = self.sess.post(
                url=f'https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.15)&_={int(self.server_time * 1000)}',
                headers=self.headers, data=data, proxies=self.proxy, verify=False, timeout=5).json()
        else:
            resp = self.sess.post(
                url=f'https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.15)&_={int(self.server_time * 1000)}',
                headers=self.headers, data=data, verify=False).json()
        self.uid = resp.get('uid')
        if resp.get('retcode') == '0':
            print(f'account {self.username} login success')
        else:
            self.login_flog = False
            print(f'account {self.username} login failed')

    def login_after(self):
        """
        登录后跳转
        """
        url = 'https://login.sina.com.cn/crossdomain2.php?action=login&entry=account&r=https%3A%2F%2Flogin.sina.com.cn%2F'
        self.headers['Host'] = 'login.sina.com.cn'
        self.headers['Referer'] = 'https://login.sina.com.cn/signup/signin.php'
        if self.proxy:
            resp = self.sess.get(url=url, headers=self.headers, proxies=self.proxy, verify=False)
        else:
            resp = self.sess.get(url=url, headers=self.headers, verify=False)
        content = json.loads(re.findall('sinaSSOController.setCrossDomainUrlList\((.*?)\)', resp.text)[0])
        self.ticket_url = content.get('arrURL')[0]

    def get_ticket(self):
        """
        登陆关键步骤，此处返回真正的cookies
        """
        cookies = ''
        url = self.ticket_url + f'&callback=sinaSSOController.doCrossDomainCallBack&scriptId=ssoscript0&client=ssologin.js(v1.4.19)&_={int(time.time() * 1000)}'
        self.headers['Host'] = 'passport.weibo.com'
        self.headers['Referer'] = 'https://login.sina.com.cn/'
        if self.proxy:
            self.sess.get(url=url, headers=self.headers, proxies=self.proxy, verify=False)
        else:
            self.sess.get(url=url, headers=self.headers, verify=False)
        cookie_dict = requests.utils.dict_from_cookiejar(self.sess.cookies)
        for key, value in cookie_dict.items():
            cookie = key + '=' + value + ';'
            cookies += cookie
        self.cookiesdict.setdefault('cookies', cookies)

    def cookies(self):
        try:
            self.before_login()
            self.get_code()
            self.login()
            if self.login_flog:
                self.login_after()
                self.get_ticket()
                return self.cookiesdict
            else:
                return False
        except Exception as e:
            if isinstance(e, requests.exceptions.ProxyError):
                return 'Proxy error'
            print(f'error: {e}')
            return False


if __name__ == '__main__':
    cookies = sn_login('13695540308', '*********').cookies()
    print(cookies)
